package net.ming616.nlp.graph.neo4j.service;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import net.ming616.nlp.graph.neo4j.model.Attribute;
import net.ming616.nlp.graph.neo4j.model.AttributeValue;
import net.ming616.nlp.graph.neo4j.model.Entity;
import net.ming616.nlp.graph.neo4j.model.EntityCategory;
import net.ming616.nlp.graph.neo4j.model.NodeType;
import net.ming616.nlp.graph.neo4j.service.repositories.AttributeRepository;
import net.ming616.nlp.graph.neo4j.service.repositories.AttributeValueRepository;
import net.ming616.nlp.graph.neo4j.service.repositories.EntityCategoryRepository;
import net.ming616.nlp.graph.neo4j.service.repositories.EntityRepository;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class EntityService {
	/**
	 * Logger for this class
	 */
	private static final Logger logger = Logger.getLogger(EntityService.class);

	@Autowired
	AttributeRepository attributeRepository;

	@Autowired
	AttributeValueRepository attributeValueRepository;

	@Autowired
	EntityCategoryRepository entityCategoryRepository;

	@Autowired
	EntityRepository entityRepository;

	public List<Entity> findEntityByAttributes(List<String> attributes) {
		List<Entity> result = new ArrayList<Entity>();
		for (String a : attributes) {
			Set<Attribute> attrs = this.attributeRepository
					.findByAttributeNameLike("*" + a + "*");
			for (Attribute attr : attrs) {
				Attribute attribute = attributeRepository.findOne(attr
						.getNodeId());
				Entity entity = this.entityRepository.findOne(attribute
						.getEntity().getNodeId());
				result.add(entity);
			}
		}
		return result;
	}

	public List<Entity> findEntityByAttributeValues(List<String> values) {
		List<Entity> result = new ArrayList<Entity>();
		for (String v : values) {
			Set<AttributeValue> attributeValues = this.attributeValueRepository
					.findByNameLike("*" + v + "*");
			for (AttributeValue attributeValue : attributeValues) {
				Attribute a = attributeValue.getAttribute();
				if (null != a) {
					a = attributeRepository.findOne(a.getNodeId());
					Entity entity = this.entityRepository.findOne(a.getEntity()
							.getNodeId());
					result.add(entity);
				}
			}
		}
		return result;
	}

	public Set<Entity> findEntityByChunks(List<Chunk> chunks) {
		List<Entity> result = new ArrayList<Entity>();
		List<String> entityList = new ArrayList<String>();
		List<String> attributeList = new ArrayList<String>();
		List<String> attributeValueList = new ArrayList<String>();

		List<List<Entity>> inputList = new ArrayList<List<Entity>>();
		for (Chunk chunk : chunks) {
			// //fetch entity by entity_category
			if (StringUtils.equals(String.valueOf(NodeType.EntityCategory),
					chunk.getType())) {
				Set<EntityCategory> categories = this.entityCategoryRepository
						.findByNameLike("*" + chunk.getText() + "*");
				for (EntityCategory c : categories) {
					Set<Entity> entities = c.getEntities();
					for (Entity e : entities) {
						result.add(e);
					}
				}

			}
			// //fetch entity by entity
			else if (StringUtils.equals(String.valueOf(NodeType.Entity),
					chunk.getType())) {
				entityList.add(chunk.getText());
			}
			// //fetch entity by attribute
			else if (StringUtils.equals(String.valueOf(NodeType.Attribute),
					chunk.getType())) {
				attributeList.add(chunk.getText());
			}
			// //fetch entity by value
			else if (StringUtils.equals(
					String.valueOf(NodeType.AttributeValue), chunk.getType())) {
				attributeValueList.add(chunk.getText());
			}
		}

		if (CollectionUtils.isNotEmpty(result)) {
			inputList.add(result);
		}

		List<Entity> tempEntities = this.findEntityByEntities(entityList);
		if (CollectionUtils.isNotEmpty(tempEntities)) {
			inputList.add(tempEntities);
		}

		List<Entity> tempAttriubtes = this
				.findEntityByAttributes(attributeList);
		if (CollectionUtils.isNotEmpty(tempAttriubtes)) {
			inputList.add(tempAttriubtes);
		}

		List<Entity> tempAttriubteValues = this
				.findEntityByAttributeValues(attributeValueList);
		if (CollectionUtils.isNotEmpty(tempAttriubteValues)) {
			inputList.add(tempAttriubteValues);
		}

		return this.maxCommon(inputList);
	}

	public List<Entity> findEntityByEntities(List<String> entities) {
		List<Entity> result = new ArrayList<Entity>();
		for (String e : entities) {
			Set<Entity> es = this.entityRepository.findByEntityNameLike("*" + e
					+ "*");
			result.addAll(es);
		}
		return result;
	}

	private Set<Entity> maxCommon(List<List<Entity>> inputList) {
		Set<Entity> all = new HashSet<Entity>();
		for (List<Entity> list : inputList) {
			all.addAll(list);
		}
		for (Entity entity : all) {
			for (List<Entity> list : inputList) {
				if (!list.contains(entity)) {
					all.remove(entity);
				}
			}
		}
		return all;
	}

}
